package com.example.docmanagement.service;

import com.example.docmanagement.dto.request.ChangePasswordRequest;
import com.example.docmanagement.dto.request.ForgotPasswordRequest;
import com.example.docmanagement.dto.request.LoginRequest;
import com.example.docmanagement.dto.request.RegisterRequest;
import com.example.docmanagement.dto.request.ResetPasswordConfirmRequest;
import com.example.docmanagement.dto.response.AuthResponse;
import com.example.docmanagement.dto.response.PasswordResetTokenResponse;
import com.example.docmanagement.entity.User;
import com.example.docmanagement.exception.DuplicateResourceException;
import com.example.docmanagement.exception.ResourceNotFoundException;
import com.example.docmanagement.repository.UserRepository;
import com.example.docmanagement.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 认证服务
 * 处理用户登录、注册等业务逻辑
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider jwtTokenProvider;
    private final AuthenticationManager authenticationManager;

    /**
     * 用户登录
     *
     * @param request 登录请求
     * @return 认证响应（包含 Token 和用户信息）
     */
    @Transactional(readOnly = true)
    public AuthResponse login(LoginRequest request) {
        log.info("用户登录: {}", request.getEmail());

        try {
            // 使用 Spring Security 进行认证
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            request.getEmail(),
                            request.getPassword()
                    )
            );

            // 认证成功，生成 Token
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();

            // 查询用户完整信息
            User user = userRepository.findByEmailOrUsername(request.getEmail(), request.getEmail())
                    .orElseThrow(() -> new ResourceNotFoundException("用户不存在"));

            // 生成 Access Token 和 Refresh Token
            String accessToken = jwtTokenProvider.generateToken(userDetails);
            String refreshToken = jwtTokenProvider.generateRefreshToken(userDetails);

            log.info("用户登录成功: {}", user.getEmail());

            return AuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .tokenType("Bearer")
                    .expiresIn(24 * 60 * 60L) // 24小时（应该从配置读取）
                    .user(AuthResponse.UserInfo.builder()
                            .id(user.getId().toString())
                            .email(user.getEmail())
                            .username(user.getUsername())
                            .fullName(user.getFullName())
                            .roleName(user.getRole() != null ? user.getRole().getDisplayName() : null)
                            .build())
                    .build();

        } catch (BadCredentialsException e) {
            log.warn("用户登录失败 - 密码错误: {}", request.getEmail());
            throw new BadCredentialsException("邮箱或密码错误");
        }
    }

    /**
     * 用户注册
     *
     * @param request 注册请求
     * @return 认证响应
     */
    @Transactional
    public AuthResponse register(RegisterRequest request) {
        log.info("用户注册: {}", request.getEmail());

        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new DuplicateResourceException("邮箱已被注册");
        }

        // 检查用户名是否已存在
        if (userRepository.existsByUsername(request.getUsername())) {
            throw new DuplicateResourceException("用户名已被使用");
        }

        // 创建新用户
        User user = new User();
        user.setEmail(request.getEmail().toLowerCase());
        user.setUsername(request.getUsername());
        user.setEncryptedPassword(passwordEncoder.encode(request.getPassword()));
        user.setFullName(request.getFullName());
        user.setStatus("active");
        user.setEmailVerified(false);
        user.setRawAppMetaData("{}");
        user.setRawUserMetaData("{}");
        user.setSignInCount(0);

        // 保存用户
        user = userRepository.save(user);

        log.info("用户注册成功: {}", user.getEmail());

        // 自动登录（生成 Token）
        UserDetails userDetails = org.springframework.security.core.userdetails.User.builder()
                .username(user.getEmail())
                .password(user.getEncryptedPassword())
                .authorities("ROLE_USER")
                .build();

        String accessToken = jwtTokenProvider.generateToken(userDetails);
        String refreshToken = jwtTokenProvider.generateRefreshToken(userDetails);

        return AuthResponse.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .tokenType("Bearer")
                .expiresIn(24 * 60 * 60L)
                .user(AuthResponse.UserInfo.builder()
                        .id(user.getId().toString())
                        .email(user.getEmail())
                        .username(user.getUsername())
                        .fullName(user.getFullName())
                        .build())
                .build();
    }

    /**
     * 刷新 Token
     *
     * @param refreshToken 刷新令牌
     * @return 新的认证响应
     */
    @Transactional(readOnly = true)
    public AuthResponse refreshToken(String refreshToken) {
        // 验证 Refresh Token
        if (!jwtTokenProvider.validateToken(refreshToken)) {
            throw new BadCredentialsException("无效的 Refresh Token");
        }

        // 从 Token 中提取用户名
        String username = jwtTokenProvider.extractUsername(refreshToken);

        // 查询用户
        User user = userRepository.findByEmailOrUsername(username, username)
                .orElseThrow(() -> new ResourceNotFoundException("用户不存在"));

        // 生成新的 Token
        UserDetails userDetails = org.springframework.security.core.userdetails.User.builder()
                .username(user.getEmail())
                .password(user.getEncryptedPassword())
                .authorities("ROLE_USER")
                .build();

        String newAccessToken = jwtTokenProvider.generateToken(userDetails);
        String newRefreshToken = jwtTokenProvider.generateRefreshToken(userDetails);

        return AuthResponse.builder()
                .accessToken(newAccessToken)
                .refreshToken(newRefreshToken)
                .tokenType("Bearer")
                .expiresIn(24 * 60 * 60L)
                .user(AuthResponse.UserInfo.builder()
                        .id(user.getId().toString())
                        .email(user.getEmail())
                        .username(user.getUsername())
                        .fullName(user.getFullName())
                        .roleName(user.getRole() != null ? user.getRole().getDisplayName() : null)
                        .build())
                .build();
    }

    /**
     * 申请重置密码
     *
     * @param request 忘记密码请求
     * @return 重置令牌响应
     */
    @Transactional
    public PasswordResetTokenResponse requestPasswordReset(ForgotPasswordRequest request) {
        String email = request.getEmail().trim().toLowerCase();
        return userRepository.findByEmail(email)
                .map(user -> {
                    String resetToken = UUID.randomUUID().toString();
                    LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(30);

                    user.setResetToken(resetToken);
                    user.setResetTokenExpiresAt(expiresAt);
                    userRepository.save(user);

                    log.info("生成密码重置令牌: email={}, expiresAt={}", email, expiresAt);

                    return PasswordResetTokenResponse.builder()
                            .resetToken(resetToken)
                            .expiresAt(expiresAt)
                            .message("开发环境：请使用该令牌调用 /api/v1/auth/password/reset 完成密码重置。生产环境应改为发送邮件。")
                            .build();
                })
                .orElseGet(() -> {
                    log.warn("尝试为不存在的邮箱申请密码重置: {}", email);
                    return PasswordResetTokenResponse.builder()
                            .resetToken(null)
                            .expiresAt(null)
                            .message("如果邮箱存在，我们会发送密码重置指引。")
                            .build();
                });
    }

    /**
     * 使用令牌重置密码
     *
     * @param request 重置确认请求
     */
    @Transactional
    public void resetPassword(ResetPasswordConfirmRequest request) {
        User user = userRepository.findByResetToken(request.getToken())
                .orElseThrow(() -> new BadCredentialsException("重置令牌无效或已过期"));

        if (user.getResetTokenExpiresAt() == null || user.getResetTokenExpiresAt().isBefore(LocalDateTime.now())) {
            throw new BadCredentialsException("重置令牌已过期");
        }

        user.setEncryptedPassword(passwordEncoder.encode(request.getNewPassword()));
        user.setResetToken(null);
        user.setResetTokenExpiresAt(null);
        userRepository.save(user);

        log.info("用户通过重置令牌更新密码成功: {}", user.getEmail());
    }

    /**
     * 登录状态下修改密码
     *
     * @param username 当前登录用户名（邮箱）
     * @param request  修改密码请求
     */
    @Transactional
    public void changePassword(String username, ChangePasswordRequest request) {
        User user = userRepository.findByEmailOrUsername(username, username)
                .orElseThrow(() -> new ResourceNotFoundException("用户不存在"));

        if (!passwordEncoder.matches(request.getCurrentPassword(), user.getEncryptedPassword())) {
            throw new BadCredentialsException("当前密码不正确");
        }

        if (passwordEncoder.matches(request.getNewPassword(), user.getEncryptedPassword())) {
            throw new BadCredentialsException("新密码不能与当前密码相同");
        }

        user.setEncryptedPassword(passwordEncoder.encode(request.getNewPassword()));
        userRepository.save(user);

        log.info("用户修改密码成功: {}", user.getEmail());
    }
}

